﻿using System;
using System.Runtime.InteropServices;

namespace dllLink
{
    [StructLayout(LayoutKind.Sequential)]
    internal class gsl_block
    {
        public uint size = 0;
        public IntPtr data = IntPtr.Zero;

    }

    [StructLayout(LayoutKind.Sequential)]
    internal class gsl_vector
    {
        public uint size = 0;
        public uint stride = 0;
        public IntPtr data = IntPtr.Zero;
        public gsl_block block;
        public int owner = 0;
    }

    [StructLayout(LayoutKind.Sequential)]
    internal class gsl_matrix
    {
        public uint size1 = 0;
        public uint size2 = 0;
        public uint tda = 0;
        public IntPtr data = IntPtr.Zero;
        public gsl_block block;
        public int owner = 0;
    }

    [StructLayout(LayoutKind.Sequential)]
    internal class gsl_permutation
    {
        public uint size = 0;
        public IntPtr data = IntPtr.Zero;
    }
    
    [StructLayout(LayoutKind.Sequential)]
    internal class gsl_rng_type
    {
        public IntPtr name;
        public UInt64 max;
        public UInt64 min;
        public uint size;
        public IntPtr set = IntPtr.Zero;
        public IntPtr get = IntPtr.Zero;
        public IntPtr get_double = IntPtr.Zero;
    }


    [StructLayout(LayoutKind.Sequential)]
    internal class gsl_rng
    {
        public IntPtr type;
        public IntPtr state = IntPtr.Zero;
    }


    internal enum CBLAS_ORDER_t : int { CblasRowMajor = 101, CblasColMajor = 102 };
    internal enum CBLAS_TRANSPOSE_t : int { CblasNoTrans = 111, CblasTrans = 112, CblasConjTrans = 113 };
    internal enum CBLAS_UPLO_t : int { CblasUpper = 121, CblasLower = 122 };
    internal enum CBLAS_DIAG_t : int { CblasNonUnit = 131, CblasUnit = 132 };
    internal enum CBLAS_SIDE_t : int { CblasLeft = 141, CblasRight = 142 };

    internal class gsl
    {
        //
        // for gsl_block
        internal static gsl_block gsl_block_alloc(uint n)
        {
            gsl_block b = new gsl_block();
            b.data = Marshal.AllocCoTaskMem((int)n * sizeof(double));
            b.size = n;
            return b;
        }
        internal static gsl_block gsl_block_calloc(uint n)
        {
            gsl_block b = new gsl_block();
            b.data = Marshal.AllocCoTaskMem((int)n * sizeof(double));
            b.size = n;

            for (int i = 0; i < sizeof(double) * (int)n; ++i)
                Marshal.WriteByte(b.data, i, 0);
            return b;
        }
        internal static void gsl_block_free(gsl_block b)
        {
            if (b != null && b.data != null)
            {
                Marshal.FreeCoTaskMem(b.data);
                b.data = IntPtr.Zero;
            }
            b.size = 0;
        }

        //
        // for gsl_vector
        //
        // memory
        internal static gsl_vector gsl_vector_alloc(uint n)
        {
            gsl_vector v = new gsl_vector();
            v.size = n;
            v.stride = 1;
            v.block = gsl_block_alloc(n);
            v.data = v.block.data;
            v.owner = 1;
            return v;
        }
        internal static gsl_vector gsl_vector_calloc(uint n)
        {
            gsl_vector v = new gsl_vector();
            v.size = n;
            v.stride = 1;
            v.block = gsl_block_calloc(n);
            v.data = v.block.data;
            v.owner = 1;
            return v;
        }
        internal static void gsl_vector_free(gsl_vector v)
        {
            if (v == null)
                return;
            gsl_block_free(v.block);
            v.data = IntPtr.Zero;
            v.size = 0;
            v.stride = 0;
            v.owner = 0;
        }
        [DllImport("libgsl.dll")]
        internal static extern int gsl_vector_isnull(gsl_vector v);
        // copy
        [DllImport("libgsl.dll")]
        internal static extern int gsl_vector_memcpy(gsl_vector dest, gsl_vector src);
        // accessor
        [DllImport("libgsl.dll")]
        internal static extern void gsl_vector_set(gsl_vector v, uint i, double x);
        [DllImport("libgsl.dll")]
        internal static extern double gsl_vector_get(gsl_vector v, uint i);
        [DllImport("libgsl.dll")]
        internal static extern void gsl_vector_set_all(gsl_vector v, double x);
        [DllImport("libgsl.dll")]
        internal static extern void gsl_vector_set_zero(gsl_vector v);
        // operator
        [DllImport("libgsl.dll")]
        internal static extern int gsl_vector_add(gsl_vector a, gsl_vector b);
        [DllImport("libgsl.dll")]
        internal static extern int gsl_vector_sub(gsl_vector a, gsl_vector b);
        [DllImport("libgsl.dll")]
        internal static extern int gsl_vector_mul(gsl_vector a, gsl_vector b);
        [DllImport("libgsl.dll")]
        internal static extern int gsl_vector_div(gsl_vector a, gsl_vector b);
        [DllImport("libgsl.dll")]
        internal static extern int gsl_vector_scale(gsl_vector a, double x);
        [DllImport("libgsl.dll")]
        internal static extern int gsl_vector_add_ant(gsl_vector a, double x);
        // min max
        [DllImport("libgsl.dll")]
        internal static extern double gsl_vector_max(gsl_vector v);
        [DllImport("libgsl.dll")]
        internal static extern double gsl_vector_min(gsl_vector v);
        [DllImport("libgsl.dll")]
        internal static extern void gsl_vector_minmax(gsl_vector v, double min_out, double max_out);
        [DllImport("libgsl.dll")]
        internal static extern uint gsl_vector_max_index(gsl_vector v);
        [DllImport("libgsl.dll")]
        internal static extern uint gsl_vector_min_index(gsl_vector v);
        [DllImport("libgsl.dll")]
        internal static extern void gsl_vector_minmax_index(gsl_vector v, uint imin, uint imax);

        //
        // for gsl_matrix
        //
        // memory
        internal static gsl_matrix gsl_matrix_alloc(uint n1, uint n2)
        {
            gsl_matrix b = new gsl_matrix();
            b.size1 = n1;
            b.size2 = n2;
            b.tda = n2;
            b.block = gsl_block_alloc(n1 * n2);
            b.data = b.block.data;
            b.owner = 1;
            return b;
        }
        internal static gsl_matrix gsl_matrix_calloc(uint n1, uint n2)
        {
            gsl_matrix b = new gsl_matrix();
            b.size1 = n1;
            b.size2 = n2;
            b.tda = n2;
            b.block = gsl_block_calloc(n1 * n2);
            b.data = b.block.data;
            b.owner = 1;
            return b;
        }
        internal static void gsl_matrix_free(gsl_matrix m)
        {
            if (m == null)
                return;
            gsl_block_free(m.block);
            m.block = null;
            m.data = IntPtr.Zero;
            m.size1 = 0;
            m.size2 = 0;
            m.tda = 0;
            m.owner = 0;
        }
        [DllImport("libgsl.dll")]
        internal static extern int gsl_matrix_isnull(gsl_matrix m);
        // copy
        [DllImport("libgsl.dll")]
        internal static extern int gsl_matrix_memcpy(gsl_matrix dest, gsl_matrix src);
        [DllImport("libgsl.dll")]
        internal static extern int gsl_matrix_transpose_memcpy(gsl_matrix dest, gsl_matrix src);
        // accessor
        [DllImport("libgsl.dll")]
        internal static extern void gsl_matrix_set(gsl_matrix m, uint i, uint j, double x);
        [DllImport("libgsl.dll")]
        internal static extern double gsl_matrix_get(gsl_matrix m, uint i, uint j);
        [DllImport("libgsl.dll")]
        internal static extern void gsl_matrix_set_all(gsl_matrix m, double x);
        [DllImport("libgsl.dll")]
        internal static extern void gsl_matrix_set_zero(gsl_matrix m);
        [DllImport("libgsl.dll")]
        internal static extern void gsl_matrix_set_identity(gsl_matrix m);
        // operator
        [DllImport("libgsl.dll")]
        internal static extern int gsl_matrix_add(gsl_matrix a, gsl_matrix b);
        [DllImport("libgsl.dll")]
        internal static extern int gsl_matrix_sub(gsl_matrix a, gsl_matrix b);
        [DllImport("libgsl.dll")]
        internal static extern int gsl_matrix_mul_elements(gsl_matrix a, gsl_matrix b);
        [DllImport("libgsl.dll")]
        internal static extern int gsl_matrix_div_elements(gsl_matrix a, gsl_matrix b);
        [DllImport("libgsl.dll")]
        internal static extern int gsl_matrix_scale(gsl_matrix a, double x);
        [DllImport("libgsl.dll")]
        internal static extern int gsl_matrix_add_ant(gsl_matrix a, double x);
        // min max
        [DllImport("libgsl.dll")]
        internal static extern double gsl_matrix_max(gsl_matrix m);
        [DllImport("libgsl.dll")]
        internal static extern double gsl_matrix_min(gsl_matrix m);
        [DllImport("libgsl.dll")]
        internal static extern void gsl_matrix_minmax(gsl_matrix m, double min_out, double max_out);
        [DllImport("libgsl.dll")]
        internal static extern void gsl_matrix_max_index(gsl_matrix m, uint imax, uint jmax);
        [DllImport("libgsl.dll")]
        internal static extern void gsl_matrix_min_index(gsl_matrix m, uint imax, uint jmax);
        [DllImport("libgsl.dll")]
        internal static extern void gsl_matrix_minmax_index(gsl_matrix m, uint imin, uint imax);

        //
        // for gsl_permutation
        //
        internal static gsl_permutation gsl_permutation_alloc(uint n)
        {
            gsl_permutation p = new gsl_permutation();
            p.data = Marshal.AllocCoTaskMem((int)n * sizeof(uint));
            p.size = n;
            return p;
        }
        internal static gsl_permutation gsl_permutation_calloc(uint n)
        {
            gsl_permutation p = new gsl_permutation();
            p.data = Marshal.AllocCoTaskMem((int)n * sizeof(uint));
            p.size = n;

            for (int i = 0; i < n; ++i)
                Marshal.WriteInt64(p.data, i, 0);
            return p;
        }
        internal static void gsl_permutation_free(gsl_permutation p)
        {
            if (p != null && p.data != IntPtr.Zero)
            {
                Marshal.FreeCoTaskMem(p.data);
                p.data = IntPtr.Zero;
            }
            p.size = 0;
        }

        [DllImport("libgsl.dll")]
        internal static extern void gsl_permutation_init(gsl_permutation p);
        [DllImport("libgsl.dll")]
        internal static extern int gsl_permutation_memcpy(gsl_permutation dest, gsl_permutation src);

        //
        // for gsl_blas
        //
        // Level 1
        [DllImport("libgsl.dll")]
        internal static extern int gsl_blas_ddot(gsl_vector x, gsl_vector y, out double result);
        [DllImport("libgsl.dll")]
        internal static extern double gsl_blas_dnrm2(gsl_vector x);
        [DllImport("libgsl.dll")]
        internal static extern double gsl_blas_dasum(gsl_vector x);
        // Level 2
        [DllImport("libgsl.dll")]
        internal static extern int gsl_blas_dgemv(CBLAS_TRANSPOSE_t TransA, double alpha,
            gsl_matrix A, gsl_vector x, double beta, gsl_vector y);
        // Level 3  
        [DllImport("libgsl.dll")]
        internal static extern int gsl_blas_dgemm(CBLAS_TRANSPOSE_t TransA, CBLAS_TRANSPOSE_t TransB, double alpha,
            gsl_matrix A, gsl_matrix B, double beta, gsl_matrix C);


        //
        // for gsl_linalg
        //
        // LU decomp
        [DllImport("libgsl.dll")]
        internal static extern int gsl_linalg_LU_decomp(gsl_matrix A, gsl_permutation p, out int signum);
        [DllImport("libgsl.dll")]
        internal static extern int gsl_linalg_LU_solve(gsl_matrix LU, gsl_permutation p, gsl_vector b, gsl_vector x);
        [DllImport("libgsl.dll")]
        internal static extern int gsl_linalg_LU_invert(gsl_matrix LU, gsl_permutation p, gsl_matrix inverse);
        [DllImport("libgsl.dll")]
        internal static extern double gsl_linalg_LU_det(gsl_matrix LU, out int signum);
        // QR decomp
        [DllImport("libgsl.dll")]
        internal static extern int gsl_linalg_QR_decomp(gsl_matrix A, gsl_vector tau);
        [DllImport("libgsl.dll")]
        internal static extern int gsl_linalg_QR_solve(gsl_matrix QR, gsl_vector tau, gsl_vector b, gsl_vector x);
        [DllImport("libgsl.dll")]
        internal static extern int gsl_linalg_QR_unpack(gsl_matrix QR, gsl_vector tau, gsl_matrix Q, gsl_matrix R);
        // SV decomp
        [DllImport("libgsl.dll")]
        internal static extern int gsl_linalg_SV_decomp(gsl_matrix A, gsl_matrix V, gsl_vector S, gsl_vector work);
    }
}